Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 2x 5x 5x 5x 5x 5x 5x 5x 1x 1x 4x 4x 4x 4x 3x 3x 2x 2x 2x 2x 2x 3x 4x 1x 1x 2x 2x 4x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | export const dynamic = 'force-dynamic'
import {prisma} from '@/lib/db'
import {requireSession} from '@/lib/guard'
import {bus, EVENTS} from '@/lib/events'
import {Prisma} from '@prisma/client'
import {addSongFromProposal} from '@/lib/songs'
import {route} from '@/lib/route'
async function withPromotion(
tx: Prisma.TransactionClient,
proposalId: string,
userId: string,
band: {id: string; voteThreshold: number; tributeTo: string | null},
) {
const threshold = band.voteThreshold
const voteCount = await tx.vote.count({where: {proposalId}})
const p = await tx.proposal.findUnique({where: {id: proposalId}})
if (!p) return
if (p.status === 'PENDING' && voteCount >= threshold) {
await tx.proposal.update({
where: {id: proposalId},
data: {status: 'APPROVED'},
})
await addSongFromProposal(tx, p, userId, band)
}
}
async function inBand(proposalId: string, bandId: string) {
const p = await prisma.proposal.findFirst({
where: {id: proposalId, bandId},
select: {id: true},
})
return Boolean(p)
}
export const POST = route(
async (_req: Request, {params}: {params: {id: string}}) => {
const {user, band} = await requireSession()
const pid = params.id
if (!(await inBand(pid, band.id)))
return new Response('Not Found', {status: 404})
try {
await prisma.$transaction(async (tx: Prisma.TransactionClient) => {
await tx.vote.create({data: {userId: user.id, proposalId: pid}})
await tx.auditLog.create({
data: {userId: user.id, action: 'VOTE', targetId: pid},
})
await withPromotion(tx, pid, user.id, band)
})
} catch {
// unique(userId, proposalId) constraint trip -> conflict
return new Response('Conflict', {status: 409})
}
bus.emit(EVENTS.PROPOSAL_UPDATED, {id: pid, bandId: band.id})
return new Response(null, {status: 204})
},
)
export const DELETE = route(
async (_req: Request, {params}: {params: {id: string}}) => {
const {user, band} = await requireSession()
const pid = params.id
if (!(await inBand(pid, band.id)))
return new Response('Not Found', {status: 404})
await prisma.$transaction(async (tx: Prisma.TransactionClient) => {
await tx.vote.delete({
where: {userId_proposalId: {userId: user.id, proposalId: pid}},
})
await tx.auditLog.create({
data: {userId: user.id, action: 'UNVOTE', targetId: pid},
})
// No auto-demote in v1
})
bus.emit(EVENTS.PROPOSAL_UPDATED, {id: pid, bandId: band.id})
return new Response(null, {status: 204})
},
)
|